#pragma once

#include "index.hpp"
#include "util.hpp"

#include <algorithm>

#include <jsoncpp/json/json.h>

namespace ns_search
{
    struct InvertedElemPrint{
        uint64_t doc_id;
        std::vector<std::string> words;
        int weight;

        InvertedElemPrint(): doc_id(0),weight(0){}

    };

    class Searcher
    {
    private:
        ns_index::Index *index; // 供系统查找的索引，

    public:
        Searcher() {}
        ~Searcher() {}

    public:
        // 创建索引
        void InitSearcher(const std::string &input)
        {
            // 获取单例类

            std::cout<<"获取索引......."<<std::endl;
            index = ns_index::Index::GetIndex();

            std::cout<<"正在创建索引......."<<std::endl;

            index->BuildIndex(input);

            std::cout<<"创建索引成功......"<<std::endl;

        }

        // query搜索关键字
        // json_string返回浏览器搜索结果
        void Serach(const std::string &query, std::string *json_string)
        {
            // 1.分词：对输入查找的词进行分词
            std::vector<std::string> words;
            util::JiebaUtil::CutString(query, &words);

            // 2.触发：根据分词进行index查找

            //ns_index::InvertedList list_all; // 保存所有分词查询到的结果

            std::unordered_map<uint64_t,InvertedElemPrint>  tokens_map;//去重保存查询到的结果

            std::vector<InvertedElemPrint> list_all;

            for (auto &s : words)
            {

                // 一个关键字可以查询出多个文档

                //我们在建立索引时统一转化为小写，这里查询也要统一转化为小写
                boost::to_lower(s);
                ns_index::InvertedList *list = index->GetInvertedIndex(s);

               

                if (nullptr == list)
                {
                    // 没有查找到相应的分词
                    continue;
                }
                else
                {

                    // 将本次查询到的分词插入
                    // 这里有一些不完美的地方，相同的分词会被插入两次
                    //我们可以将搜索中相同的分词合并，统计次数，然后再设置weigth时乘上次数

                   // list_all.insert(list_all.end(), list->begin(), list->end());
                   for(auto &elem:*list)
                   {
                    auto& em=tokens_map[elem.doc_id];
                    em.doc_id=elem.doc_id;
                    em.weight+=elem.weight;
                    em.words.push_back(elem.world);
                   }

                }
                
            }

            for(auto& item: tokens_map)
            {
                list_all.push_back(std::move(item.second));
            }


            // 3.合并排序：按照权值进行合并排序

            // 这里我们可以创建一个unordered_map,保存相应的文档id和次数，避免文档重复(后面进行补充修改)
            // std::sort(list_all.begin(), list_all.end(), [](const ns_index::InvertedElem e1, const ns_index::InvertedElem e2)
            //           { return e1.weight > e2.weight; });

            std::sort(list_all.begin(), list_all.end(), [](const InvertedElemPrint e1, const InvertedElemPrint e2)
                      { return e1.weight > e2.weight; });
                      

            // 4.构建：将查找出来的结果构建成json串格式----引入外部Jason库函数

            // 根据doc_id进行正排索引查找

            Json::Value root;

            for (auto &item : list_all)
            {
                ns_index::Doc_Info *doc = index->GetForwardIndex(item.doc_id);

                if (nullptr == doc)
                {
                    // 不存在该文件
                    continue;
                }

                Json::Value elem;

                elem["title"] = doc->title;
                //elem["body"] = doc->body;
                //这里我们需要的不是整个html内容，而是包含关键字的一小部分作为摘要
                elem["body"]=GetBody(doc->body,item.words[0]);

                elem["url"] = doc->url;

                elem["weigth"]=item.weight;

                root.append(elem);
            }

            Json::StyledWriter writer;

            *json_string = writer.write(root);
        }

        std::string GetBody(const std::string& html_body,const std::string& world)
        {
            const std::size_t prev_step=50;
            const std::size_t next_step=100;

            //找到world出现的位置往前50个字符，往后100个字符

            //这里我们的body里面保存的是有大写和小写的区别，find区分大小写，会导致查找出现偏差
            //std::size_t pos=html_body.find(world);

            auto item=std::search(html_body.begin(),html_body.end(),world.begin(),world.end(),[](int x,int y){
                return (std::tolower(x)==std::tolower(y));
            });

            if(item==html_body.end())
            {
                //这里基本上不可能出现，除非程序出现了问题
                std::cout<<"code error"<<std::endl;
                return "None";
            }

            std::size_t start=0;
            std::size_t end=html_body.size()-1;

            std::size_t pos=std::distance(html_body.begin(),item);

            if(pos>prev_step)
            {
                start=pos-prev_step;
            }

            if(end-pos>next_step)
            {
                end=pos+next_step;
            }

            if(start>end)
            {
                return "";
            }
            return html_body.substr(start,end-start)+".....";
        }

    };

}